package com.suncky.frame.utils;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import android.util.Base64;
import android.webkit.MimeTypeMap;

import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;

import com.google.gson.reflect.TypeToken;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.List;

/**
 * 文件相关操作
 *
 * @author cKF71412
 */
public class FileUtil {
    private static final int READ_SIZE = 8192;
    private static final String TAG = "FileUtil";

    private static final String[][] MIME_MAP = {
            //{后缀名，MIME类型}
            {".3gp", "video/3gpp"},
            {".apk", "application/vnd.android.package-archive"},
            {".asf", "video/x-ms-asf"},
            {".avi", "video/x-msvideo"},
            {".bin", "application/octet-stream"},
            {".bmp", "image/bmp"},
            {".c", "text/plain"},
            {".class", "application/octet-stream"},
            {".conf", "text/plain"},
            {".cpp", "text/plain"},
            {".doc", "application/msword"},
            {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
            {".xls", "application/vnd.ms-excel"},
            {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
            {".exe", "application/octet-stream"},
            {".gif", "image/gif"},
            {".gtar", "application/x-gtar"},
            {".gz", "application/x-gzip"},
            {".h", "text/plain"},
            {".htm", "text/html"},
            {".html", "text/html"},
            {".jar", "application/java-archive"},
            {".java", "text/plain"},
            {".jpeg", "image/jpeg"},
            {".jpg", "image/jpeg"},
            {".webp", "image/webp"},
            {".js", "application/x-javascript"},
            {".log", "text/plain"},
            {".m3u", "audio/x-mpegurl"},
            {".m4a", "audio/mp4a-latm"},
            {".m4b", "audio/mp4a-latm"},
            {".m4p", "audio/mp4a-latm"},
            {".m4u", "video/vnd.mpegurl"},
            {".m4v", "video/x-m4v"},
            {".mov", "video/quicktime"},
            {".mp2", "audio/x-mpeg"},
            {".mp3", "audio/x-mpeg"},
            {".mp4", "video/mp4"},
            {".mpc", "application/vnd.mpohun.certificate"},
            {".mpe", "video/mpeg"},
            {".mpeg", "video/mpeg"},
            {".mpg", "video/mpeg"},
            {".mpg4", "video/mp4"},
            {".mpga", "audio/mpeg"},
            {".msg", "application/vnd.ms-outlook"},
            {".ogg", "audio/ogg"},
            {".pdf", "application/pdf"},
            {".png", "image/png"},
            {".pps", "application/vnd.ms-powerpoint"},
            {".ppt", "application/vnd.ms-powerpoint"},
            {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
            {".prop", "text/plain"},
            {".rc", "text/plain"},
            {".rmvb", "audio/x-pn-realaudio"},
            {".rtf", "application/rtf"},
            {".sh", "text/plain"},
            {".tar", "application/x-tar"},
            {".tgz", "application/x-compressed"},
            {".txt", "text/plain"},
            {".wav", "audio/x-wav"},
            {".wma", "audio/x-ms-wma"},
            {".wmv", "audio/x-ms-wmv"},
            {".wps", "application/vnd.ms-works"},
            {".xml", "text/plain"},
            {".z", "application/x-compress"},
            {".zip", "application/x-zip-compressed"},
            {"", "*/*"}
    };

    /**
     * 删除文件
     *
     * @param destPath 目标文件路径
     * @return 文件删除成功返回true，否则返回false
     */
    public static boolean deleteFile(String destPath) {
        boolean bResult = false;
        File file = createFile(destPath);
        if (file.exists()) {
            bResult = file.delete();
        }
        return bResult;
    }

    /**
     * 写文件
     *
     * @param content  需要写入的内容
     * @param destPath 目标文件目录
     * @return 文件存储成功返回true，否则返回false
     */
    public static boolean writeFile(String content, String destPath) {
        return writeFile(content, destPath, false);
    }

    /**
     * 写文件
     *
     * @param content  需要写入的内容
     * @param destPath 目标文件目录
     * @param append   是否追加到文件结尾
     * @return 文件存储成功返回true，否则返回false
     */
    public static boolean writeFile(String content, String destPath,
                                    boolean append) {
        OutputStream os = null;
        File file = createFile(destPath);
        boolean result = false;
        try {
            os = new FileOutputStream(file, append);
            byte[] bt = content.getBytes(StandardCharsets.UTF_8);
            os.write(bt);
            result = true;
        } catch (Exception e) {
            LogUtils.d(TAG, e.toString());
            result = false;
        } finally {
            close(os);
        }
        return result;
    }

    /**
     * 写文件
     *
     * @param is       原始文件流
     * @param destPath 目标文件目录
     * @return 文件存储成功返回true，否则返回false
     */
    public static boolean writeFile(InputStream is, String destPath) {
        return writeFile(is, destPath, false);
    }

    /**
     * 写文件
     *
     * @param is       原始文件流
     * @param destPath 目标文件目录
     * @param append   是否追加到文件结尾
     * @return 文件存储成功返回true，否则返回false
     */
    public static boolean writeFile(InputStream is, String destPath, boolean append) {
        if (is == null) {
            return false;
        }
        File file = createFile(destPath);
        BufferedInputStream bis = null;
        BufferedOutputStream os = null;
        boolean result = false;
        try {
            bis = new BufferedInputStream(is);
            os = new BufferedOutputStream(new FileOutputStream(file, append));
            byte[] bt = new byte[READ_SIZE];
            int count = 0;
            while ((count = is.read(bt)) > 0) {
                os.write(bt, 0, count);
            }
            os.flush();
            result = true;
        } catch (Exception e) {
            result = false;
        } finally {
            close(os);
            close(bis);
        }
        return result;
    }

    /**
     * 读取文件返回字符串
     * @param path 要读取的文件路径
     * @return
     */
    public static String readFileToString(String path) {
        return readFileToString(new File(path));
    }

    /**
     * 读取文件返回字符串
     * @param file 要读取的文件
     * @return
     */
    public static String readFileToString(File file) {
        InputStream is = readFile(file);
        if (is == null) {
            return null;
        }
        try {
            //创建一个字符流大小的数组。
            byte[] data = new byte[is.available()];
            //写入数组
            is.read(data);
            return new String(data, StandardCharsets.UTF_8);
        } catch (Exception e) {
            LogUtils.d(TAG, e.toString());
        } finally {
            close(is);
        }
        return null;
    }

    /**
     * 读取文件返回字符串
     * @param is 要读取的文件流
     * @return
     */
    public static String readFileToString(InputStream is) {
        if (is == null) {
            return null;
        }
        try {
            //创建一个字符流大小的数组。
            byte[] data = new byte[is.available()];
            //写入数组
            is.read(data);
            return new String(data, StandardCharsets.UTF_8);
        } catch (Exception e) {
            LogUtils.d(TAG, e.toString());
        } finally {
            close(is);
        }
        return null;
    }

    /**
     * 读取文件返回InputStream
     * @param path 要读取的文件路径
     * @return
     */
    public static InputStream readFile(String path) {
        return readFile(new File(path));
    }

    /**
     * 读取文件返回InputStream
     * @param file 要读取的文件对象
     * @return
     */
    public static InputStream readFile(File file) {
        if (!file.exists()) {
            return null;
        }
        InputStream is = null;
        try {
            is = new FileInputStream(file);
        } catch (Exception e) {
            close(is);
        }
        return is;
    }

    /**
     * 判断文件是是否存在
     * @param filePath 文件路径
     * @return
     */
    public static boolean isExists(String filePath) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Path path = Paths.get(filePath);
            return Files.exists(path);
        }else {
            return isExists(new File(filePath));
        }
    }

    /**
     * 判断文件是否存在
     * @param uri 文件uri对象
     * @return
     */
    public static boolean isExists(URI uri) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Path path = Paths.get(uri);
            return Files.exists(path);
        }else {
            return isExists(new File(uri.getPath()));
        }
    }

    /**
     * 判断文件是否存在
     * @param file 文件对象
     * @return
     */
    public static boolean isExists(File file) {
        if (file == null) {
            return false;
        }
        return file.exists();
    }

    /**
     * 创建文件目录
     *
     * @param path
     */
    public static boolean createDirectory(String path) {
        try {
            File file = new File(path);
            if (!file.exists() || !file.isDirectory()) {
                return file.mkdirs();
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 创建文件
     *
     * @param path 目标文件路径
     * @return 目标文件File对象
     */
    public static File createFile(String path) {
        File file = new File(path);
        try {
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
                file.createNewFile();
            } else if (!file.exists()) {
                file.createNewFile();
            }
        } catch (Exception e) {
            LogUtils.d(TAG, e.toString());
        }
        return file;
    }

    private static void close(InputStream is) {
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                LogUtils.d(TAG, e.toString());
            }
        }
    }

    private static void close(OutputStream os) {
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                LogUtils.d(TAG, e.toString());
            }
        }
    }


    /**
     * 转换文件大小
     *
     * @param fileS
     * @return
     */
    public static String formatFileSize(long fileS) {
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSizeString = "";
        String wrongSize = "0B";
        if (fileS == 0) {
            return wrongSize;
        }
        if (fileS < 1024) {
            fileSizeString = df.format((double) fileS) + "B";
        } else if (fileS < 1048576) {
            fileSizeString = df.format((double) fileS / 1024) + "KB";
        } else if (fileS < 1073741824) {
            fileSizeString = df.format((double) fileS / 1048576) + "MB";
        } else {
            fileSizeString = df.format((double) fileS / 1073741824) + "GB";
        }
        return fileSizeString;
    }

    /**
     * 从路径中获取文件名，不去除后缀名
     *
     * @param path
     * @return
     */
    public static String getFileName(String path) {
        if (path == null || path.length() == 0) {
            return null;
        }
        int startIndex = path.lastIndexOf("/") + 1;
        if (startIndex == path.length()) {
            return null;
        }
        return path.substring(startIndex);
    }

    /**
     * 从路径中获取文件名，无后缀名
     *
     * @param path
     * @return
     */
    public static String getFileNameNoPostfix(String path) {
        if (path == null || path.length() == 0) {
            return null;
        }
        int startIndex = path.lastIndexOf("/") + 1;
        if (startIndex == path.length()) {
            return null;
        }
        int endIndex = path.lastIndexOf(".");
        if (endIndex <= startIndex) {
            return null;
        }
        return path.substring(startIndex, endIndex);
    }

    /**
     * 从路径中获取文件后缀名,不包含"."
     * @param path 文件路径
     * @return
     */
    public static String getFilePostfix(String path) {
        if (path == null || path.length() == 0) {
            return null;
        }
        int pointIndex = path.lastIndexOf(".");
        if (pointIndex < 0 || pointIndex + 1 == path.length()) {
            return null;
        }
        return path.substring(pointIndex + 1);
    }

    /**
     * 从路径中获取文件后缀名
     *
     * @param path  文件路径
     * @param point 是否包含"."
     * @return
     */
    public static String getFilePostfix(String path, boolean point) {
        if (path == null || path.length() == 0) {
            return null;
        }
        int pointIndex = path.lastIndexOf(".");
        if (pointIndex < 0 || pointIndex + 1 == path.length()) {
            return null;
        }
        if (point) {
            return path.substring(pointIndex);
        }
        return path.substring(pointIndex + 1);
    }

    /**
     * 从文件中获取文件后缀名,不包含"."
     * @param file 文件对象
     * @return
     */
    public static String getFilePostfix(File file) {
        return getFilePostfix(file,false);
    }

    /**
     * 从文件中获取文件后缀名
     * @param file 文件对象
     * @param point 是否包含"."
     * @return
     */
    public static String getFilePostfix(File file, boolean point) {
        if (file == null || file.isDirectory()) {
            return null;
        }
        return getFilePostfix(file.getPath(),point);
    }

    /**
     * 获取文件的MIMEType
     * @param path 文件路径
     * @return
     */
    public static String getMIMEType(String path) {
        String postfix = getFilePostfix(path);
        if (postfix == null) {
            return "application/*";
        }
        String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(postfix.toLowerCase());
        if (type != null && !type.isEmpty()) {
            return type;
        }
        return "application/*";
    }

    /**
     * 获取文件的MIMEType
     * @param file
     * @return
     */
    public static String getMIMEType(File file) {
        if (file == null) {
            return getMIMEType("");
        }
        return getMIMEType(file.getPath());
    }


    /**
     * 从路径中获取文件所在的目录
     *
     * @param path
     * @return
     */
    public static String getFileDir(String path) {
        if (path == null || path.length() == 0) {
            return null;
        }
        int endIndex = path.lastIndexOf("/") + 1;
        if (endIndex == 0) {
            return null;
        }
        return path.substring(0, endIndex);
    }


    /**
     * 获取文件uri
     *
     * @param context
     * @param path      文件路径
     * @param authority manifest中定义的provider中的authorities属性名称
     * @return
     */
    public static Uri getUriForFile(@NonNull Context context, @NonNull String path, @NonNull String authority) {
        return getUriForFile(context, new File(path), authority);
    }

    /**
     * 获取文件uri
     *
     * @param context
     * @param path    文件路径
     * @return
     */
    public static Uri getUriForFile(@NonNull Context context, @NonNull String path) {
        return getUriForFile(context, new File(path));
    }

    /**
     * 根据文件获取uri
     *
     * @param context
     * @param file
     * @return
     */
    public static Uri getUriForFile(@NonNull Context context, @NonNull File file) {
        Uri fileUri;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            fileUri = FileProvider.getUriForFile(context, context.getApplicationInfo().packageName + ".provider", file);
        } else {
            fileUri = Uri.fromFile(file);
        }
        return fileUri;
    }

    /**
     * 获取文件uri
     *
     * @param context
     * @param file
     * @param authority manifest中定义的provider中的authorities属性名称
     * @return
     */
    public static Uri getUriForFile(@NonNull Context context, @NonNull File file, @NonNull String authority) {
        Uri fileUri;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            fileUri = FileProvider.getUriForFile(context, authority, file);
        } else {
            fileUri = Uri.fromFile(file);
        }
        return fileUri;
    }

    /**
     * uri授权
     *
     * @param context
     * @param intent
     * @param uri
     * @param writeAble 是否可读
     */
    public static void grantPermissions(Context context, Intent intent, Uri uri, boolean writeAble) {
        int flag = Intent.FLAG_GRANT_READ_URI_PERMISSION;
        if (writeAble) {
            flag |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
        }
        intent.addFlags(flag);
        List<ResolveInfo> resInfoList = context.getPackageManager()
                .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            String packageName = resolveInfo.activityInfo.packageName;
            context.grantUriPermission(packageName, uri, flag);
        }
    }


    /**
     * 将图片转换成Base64编码的字符串
     */
    public static String imageToBase64(String path) {
        if (TextUtils.isEmpty(path)) {
            return null;
        }
        InputStream is = null;
        byte[] data = null;
        String result = null;
        try {
            is = new FileInputStream(path);
            //创建一个字符流大小的数组。
            data = new byte[is.available()];
            //写入数组
            is.read(data);
            //用默认的编码格式进行编码
            result = Base64.encodeToString(data, Base64.NO_CLOSE);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
        return result;
    }

    /**
     * 将文件转换成Base64编码的字符串
     */
    public static String fileToBase64(String path) {
        if (TextUtils.isEmpty(path)) {
            return null;
        }
        InputStream is = null;
        String result = null;
        try {
            is = new FileInputStream(path);
            //创建一个字符流大小的数组。
            byte[] data = new byte[is.available()];
            //写入数组
            is.read(data);
            //用默认的编码格式进行编码
            result = Base64.encodeToString(data, Base64.DEFAULT);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
        return result;
    }

    /**
     * 将文件转换成Base64编码的字符串
     */
    public static String fileToBase64(File file) {
        if (file == null) {
            return null;
        }
        InputStream is = null;
        String result = null;
        try {
            is = new FileInputStream(file);
            //创建一个字符流大小的数组。
            byte[] data = new byte[is.available()];
            //写入数组
            is.read(data);
            //用默认的编码格式进行编码
            result = Base64.encodeToString(data, Base64.DEFAULT);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
        return result;
    }

    /**
     * 文件转成byte数组
     * @param path 文件路径
     * @return
     */
    public static byte[] fileToByteArray(String path) {
        if (TextUtils.isEmpty(path)) {
            return null;
        }
        return fileToByteArray(new File(path));
    }

    /**
     * 文件转成byte数组
     * @param file 文件
     * @return
     */
    public static byte[] fileToByteArray(File file) {
        if (file == null) {
            return null;
        }
        BufferedInputStream bis = null;
        byte[] result = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(file));
            result = new byte[bis.available()];
            bis.read(result);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    /**
     * 把文件发送到第三方应用
     * @param context
     * @param file 文件
     */
    public static void sendFile(Context context, File file) {
        if (context == null || file == null) {
            return;
        }
        Intent sendIntent = new Intent();
        sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        sendIntent.setAction(Intent.ACTION_SEND);
        Uri uri = FileUtil.getUriForFile(context, file);
        sendIntent.putExtra(Intent.EXTRA_STREAM, uri);
        sendIntent.setType(getMIMEType(file));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            FileUtil.grantPermissions(context, sendIntent, uri, true);
        }
        context.startActivity(Intent.createChooser(sendIntent, ""));
    }
}
